﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Transactions;

namespace CTC
{
    /// <summary>
    /// 设备
    /// </summary>
    public abstract class CTCDevice
    {
        #region 事件
        /// <summary>
        /// 数据发送失败
        /// </summary>
        public event CTCEvent.InstFailDataEvent OnSendFail;
        /// <summary>
        /// 数据发送成功
        /// </summary>
        public event CTCEvent.InstSuccessDataEvent OnSendSuccess;
        /// <summary>
        /// 串口打开
        /// </summary>
        public event CTCEvent.MessageEvent OnSerialOpen;
        /// <summary>
        /// 串口关闭
        /// </summary>
        public event CTCEvent.MessageEvent OnSerialClose;
        /// <summary>
        /// 调用信息
        /// </summary>
        public event CTCEvent.MessageEvent DebugLogI;
        /// <summary>
        /// 错误信息
        /// </summary>
        public event CTCEvent.MessageEvent DebugLogE;
        #endregion

        #region 属性
        /// <summary>
        /// 主设备版本号
        /// </summary>
        public UInt16 CTC_MASTER_VERSION = 1;
        /// <summary>
        /// 从设备版本号
        /// </summary>
        public UInt16 CTC_SLAVE_VERSION = 1;
        /// <summary>
        /// 发送事务ID，从1开始，每次发送自增
        /// </summary>
        protected UInt32 SendTransactionId = 1;
        /// <summary>
        /// 接收事务ID，从0开始，用于防止重复处理
        /// </summary>
        protected UInt32 ReceiveTransactionId = 0;
        /// <summary>
        /// 重新连接次数计数器
        /// </summary>
        public int ReconnectNumber { get; protected set; }
        /// <summary>
        /// 总接收字节数统计
        /// </summary>
        public long TotalReceivedBytes { get; protected set; }
        /// <summary>
        /// 总发送字节数统计
        /// </summary>
        public long TotalSendBytes { get; protected set; }
        /// <summary>
        /// 总发送正确消息数统计
        /// </summary>
        public long TotalSendMsgCount { get; protected set; }
        /// <summary>
        /// 总发送错误消息数统计
        /// </summary>
        public long TotalSendErrorMsgCount { get; protected set; }
        /// <summary>
        /// 总接收正确消息数统计
        /// </summary>
        public long TotalReceivedMsgCount { get; protected set; }
        /// <summary>
        /// 总接收错误消息数统计
        /// </summary>
        public long TotalReceivedErrorMsgCount { get; protected set; }
        /// <summary>
        /// 主控产品ID，用于标识当前设备
        /// </summary>
        public UInt32 MasterProductId { get; protected set; }
        /// <summary>
        /// 从产品备ID，用于标识连接的从设备
        /// </summary>
        public UInt32 SlaveProductId { get; protected set; }
        /// <summary>
        /// 主控设备ID，用于标识当前设备
        /// </summary>
        protected byte[] MasterDeviceId = new byte[16];
        /// <summary>
        /// 从设备ID，用于标识连接的从设备
        /// </summary>
        protected byte[] SlaveDeviceId = new byte[16];
        /// <summary>
        /// 主设备版本
        /// </summary>
        public UInt16 MasterCTCversion { get; protected set; }
        /// <summary>
        /// 从设备版本
        /// </summary>
        public UInt16 SlaveCTCversion { get; protected set; }
        /// <summary>
        /// 设备连接状态
        /// </summary>
        public CTCClientConnState DeviceConnState { get; protected set; }
        /// <summary>
        /// 是否已经握手
        /// </summary>
        protected bool isHandshake;

        /// <summary>
        /// 绑定的Com连接
        /// </summary>
        protected SerialPort BindCom;

        /// <summary>
        /// 数据包管理
        /// </summary>
        private DataManager DataManager;

        /// <summary>
        /// 串口是否连接
        /// </summary>
        public bool SerialConnected { get; protected set; }

        /// <summary>
        /// 收到的数据缓存区
        /// </summary>
        private byte[] BufferData = new byte[1024];

        /// <summary>
        /// 数据缓存区当前存放的数据长度
        /// </summary>
        private int BufferDataLenght;
        /// <summary>
        /// 最后一次收到指令时间（毫秒）
        /// </summary>
        public long LastReceiveTime { get; protected set; }
        /// <summary>
        /// 最后检查连接时间（毫秒）
        /// </summary>
        public long LastCheckConnectTime { get; protected set; }

        /// <summary>
        /// 最后发送数据时间
        /// </summary>
        private long LastSendTime;

        /// <summary>
        /// 最大重发次数
        /// </summary>
        private int MaxReNumber;
        /// <summary>
        /// 重发次数
        /// </summary>
        private int ReNumber;
        /// <summary>
        /// 心跳间隔(毫秒)
        /// </summary>
        private int HearBeratTime = 10000;
        /// <summary>
        /// 离线时间(毫秒)
        /// </summary>
        private int OfflineTime = 25000;
        /// <summary>
        /// 重发间隔
        /// </summary>
        private int ResendTime = 400;
        /// <summary>
        /// 发送的包
        /// </summary>
        private DataPacket SendDataPacket = null;

        /// <summary>
        /// 主设备状态
        /// </summary>
        public byte MasterState { get; protected set; }

        /// <summary>
        /// 从设备状态
        /// </summary>
        public byte SlaveState { get; protected set; }

        /// <summary>
        /// 从设备错误码
        /// </summary>
        public UInt16 SlaveErrorCode { get; protected set; }
        /// <summary>
        /// P位
        /// </summary>
        public byte Player { get; protected set; }
        /// <summary>
        /// 串口号
        /// </summary>
        public String ComName { get; protected set; }
        /// <summary>
        /// 串口发送数据线程
        /// </summary>
        private Thread SendT;
        /// <summary>
        /// 串口接收数据线程
        /// </summary>
        private Thread ReceiveT;
        #endregion

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="player">P位</param>
        /// <param name="comName">串口名</param>
        /// <param name="baudRate">波特率</param>
        public CTCDevice(byte player, String comName, int baudRate)
        {
            Player = player;
            ComName = comName;
            DataManager = new DataManager();
            MaxReNumber = 3;
            BindCom = new SerialPort(comName, baudRate);
        }

        /// <summary>
        /// 打开
        /// </summary>
        public virtual void Open()
        {
            if (!SerialConnected)
            {
                try
                {
                    BindCom.Close();
                    Thread.Sleep(20);
                    BindCom.Open();
                    SerialConnected = true;
                    LastReceiveTime = CurrentTimeMillis();
                    LastSendTime = CurrentTimeMillis();
                    LastCheckConnectTime = CurrentTimeMillis();
                    isHandshake = false;
                    SendT = new Thread(new ThreadStart(SendThread));
                    ReceiveT = new Thread(new ThreadStart(ReceiveTherad));
                    SendT.Priority = ThreadPriority.AboveNormal;
                    ReceiveT.Priority = ThreadPriority.AboveNormal;
                    SendT.Start();
                    ReceiveT.Start();
                    OnSerialOpen?.Invoke(Player, null);
                    LogI("串口打开成功");
                }
                catch (Exception e)
                {
                    SerialConnected = false;
                    LogE("串口打开失败:" + e.ToString());
                    OnSerialOpen?.Invoke(Player, e.ToString());
                }
            }
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        public virtual void Close()
        {
            if (BindCom != null)
            {
                SerialConnected = false;
                BindCom.Close();
                BindCom = null;
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data">数据</param>
        protected void Send(DataPacket packet)
        {
            DataManager.AddData(packet);
        }
        /// <summary>
        /// 发送ACK
        /// </summary>
        /// <param name="data">数据</param>
        protected void SendACK(DataPacket packet)
        {
            //只有在握手完成或握手指令才回应ACK
            if (isHandshake || CheckInst(CTCMsg.CTC_CMD_HANDSHAKE, new byte[] { packet.Data[0], packet.Data[1] }) || CheckInst(CTCMsg.CTC_CMD_HANDSHAKE_RSP, new byte[] { packet.Data[0], packet.Data[1] }))
            {
                DataManager.AddACKData(packet);
            }
        }

        /// <summary>
        /// 真正的发送数据
        /// </summary>
        /// <param name="data">数据</param>
        private void RealSend(DataPacket packet)
        {
            if (packet == null)
            {
                return;
            }
            byte[] sendData = packet.ToArray();
            BindCom.Write(sendData, 0, sendData.Length);
            LogI(String.Format("发送--" + CRC.ToHexString(sendData)));
            LastSendTime = CurrentTimeMillis();
            TotalSendBytes += sendData.Length;
        }

        /// <summary>
        /// 发送数据线程
        /// </summary>
        private void SendThread()
        {
            Thread.Sleep(100);
            while (SerialConnected)
            {
                try
                {
                    Thread.Sleep(10);
                    lock (this)
                    {
                        //如果有ACK要回应就直接发
                        if (DataManager.HasAckData())
                        {
                            RealSend(DataManager.GetAckData());
                            continue;
                        }
                        if (SendDataPacket == null)
                        {//如果没有正在发送的包就取新的
                            if (!DataManager.HasData())
                            {
                                continue;
                            }
                            SendDataPacket = DataManager.GetData();
                            RealSend(SendDataPacket);
                            ReNumber = 1;
                        }
                        else
                        {//如果有正在发送的包检查是不是超时了
                            if (CurrentTimeMillis() - LastSendTime > ResendTime)
                            {
                                if (ReNumber < MaxReNumber)
                                {
                                    RealSend(SendDataPacket);
                                    ReNumber++;
                                    LogI("重发数据");
                                }
                                else
                                {
                                    HandleSendFail(CTCMsg.CTC_ACK_TIMEOUT, SendDataPacket);
                                    LogE("重发数据失败");
                                    SendDataPacket = null;
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Close();
                    OnSerialClose?.Invoke(Player, "串口已关闭-写入数据出错:" + e.ToString());
                    return;
                }
            }
        }
        /// <summary>
        /// 接收数据线程
        /// </summary>
        private void ReceiveTherad()
        {
            Thread.Sleep(100);
            int readLenght = 0;
            byte[] readData = new byte[256];
            while (SerialConnected)
            {
                try
                {
                    lock (this)
                    {
                        if (BindCom.BytesToRead > 0)
                        {
                            readLenght = BindCom.Read(readData, 0, readData.Length);
                            TotalReceivedBytes += readLenght;
                            if (BufferDataLenght + readLenght > BufferData.Length)
                            {
                                BufferDataLenght = 0;
                            }
                            Array.Copy(readData, 0, BufferData, BufferDataLenght, readLenght);
                            BufferDataLenght += readLenght;
                            while (IsDataLegal()) ;

                        }
                        else
                        {
                            if (CurrentTimeMillis() - LastCheckConnectTime > HearBeratTime && SendDataPacket == null && DataManager.IsEmpty())
                            {
                                LastCheckConnectTime = CurrentTimeMillis();
                                HandleTimeOut();
                            }
                            else if (CurrentTimeMillis() - LastReceiveTime > OfflineTime && DeviceConnState == CTCClientConnState.Connected)
                            {
                                HandleOffline();
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Close();
                    OnSerialClose?.Invoke(Player, "串口读取数据出错:" + e.ToString());
                    break;
                }
                Thread.Sleep(10);
            }
        }

        /// <summary>
        /// 判断数据是否合法,并进行一次数据解析
        /// </summary>
        /// <returns>是否进行下一次判断</returns>
        private bool IsDataLegal()
        {
            //检查是否达到最小包长度
            if (BufferDataLenght < DataPacket.PACKET_LENGHT)
            {
                return false;
            }
            //检查包头是否正确，出错就重新寻找包头
            if (BufferData[0] != DataPacket.DATA_HEAD[0] || BufferData[1] != DataPacket.DATA_HEAD[1])
            {
                LogE("数据包头错误,重新寻找包头");
                for (int i = 2; i < BufferDataLenght - 1; i++)
                {
                    if (BufferData[i] == DataPacket.DATA_HEAD[0] && BufferData[i + 1] == DataPacket.DATA_HEAD[1])
                    {
                        LogI("找到数据包头了");
                        BufferDataLenght -= i;
                        Array.Copy(BufferData, i, BufferData, 0, BufferDataLenght);
                        return true;
                    }
                    if (i == BufferDataLenght - 2)
                    {
                        BufferDataLenght = 0;
                        LogE("没有找到数据包头,清空缓存");
                        return false;
                    }
                }
            }
            // 读取P位
            byte player = BufferData[2];
            // 读取命令标识
            byte[] inst = new byte[] { BufferData[3], BufferData[4] };
            // 读取事务ID
            byte[] packetID = new byte[] { BufferData[5], BufferData[6], BufferData[7], BufferData[8] };
            // 读取长度
            byte dateLen = BufferData[9];
            int totalData = DataPacket.PACKET_LENGHT + dateLen;
            if (BufferDataLenght < totalData)
            {
                return false;
            }
            //检验数据包CRC
            UInt32 crc0 = CRC.CRC32(BufferData, 0, 10 + dateLen);
            UInt32 crc1 = CRC.BytesToUint32(BufferData[10 + dateLen], BufferData[10 + dateLen + 1], BufferData[10 + dateLen + 2], BufferData[10 + dateLen + 3]);
            if (crc0 != crc1)
            {
                BufferDataLenght -= 2;
                Array.Copy(BufferData, 2, BufferData, 0, BufferDataLenght);
                LogE("数据包CRC错误，删除指令头，开始下一次判断");
                return true;
            }
            //检查通过，从缓存中取出数据，开始进行数据操作
            LastCheckConnectTime = CurrentTimeMillis();
            LastReceiveTime = CurrentTimeMillis();
            byte[] tempdata = new byte[totalData];
            Array.Copy(BufferData, 0, tempdata, 0, totalData);
            BufferDataLenght -= totalData;
            Array.Copy(BufferData, totalData, BufferData, 0, BufferDataLenght);
            //
            LogI(String.Format("收到--{0} ", CRC.ToHexString(tempdata)));
            CheckACK(DataPacket.Format(tempdata));
            return true;
        }
        /// <summary>
        /// 检查指令类型
        /// </summary>
        /// <param name="dataPacket"></param>
        private void CheckACK(DataPacket dataPacket)
        {
            //处理ACK
            if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_ACK))
            {//ACK回应
                if (dataPacket.DataLenght != 3)
                {
                    LogE(String.Format("CTC ACK Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                HandleAckResponse(dataPacket);
                return;
            }
            //如果事务ID为1就重新开始
            if (dataPacket.TransactionId == 1)
            {
                ReceiveTransactionId = 0;
            }
            if (dataPacket.TransactionId == ReceiveTransactionId)
            {
                // 重复的事务ID,丢弃此帧数据,但回应ACK
                LogE(String.Format("CTC Receive Buffer Duplicate TransactionID:{0}", dataPacket.TransactionId));
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                return;
            }
            //////////////////////////////
            ReceiveTransactionId = dataPacket.TransactionId;
            HandleReceive(dataPacket);
        }
        /// <summary>
        /// 毫秒时间戳
        /// </summary>
        /// <returns></returns>
        public static long CurrentTimeMillis()
        {
            return DateTime.Now.Ticks / 10000L;
        }
        /// <summary>
        /// 获取离线时间
        /// </summary>
        /// <returns></returns>
        public long GetOfflineTime() { return CurrentTimeMillis() - LastReceiveTime; }
        /// <summary>
        /// 获取重试次数
        /// </summary>
        /// <returns></returns>
        public int GetReConnectTimes() { return ReconnectNumber; }

        /// <summary>
        /// 处理离线
        /// </summary>
        protected virtual void HandleOffline()
        {
            isHandshake = false;
            DeviceConnState = CTCClientConnState.Connecting;
        }

        /// <summary>
        /// 处理超时
        /// </summary>
        protected virtual void HandleTimeOut() { }

        /// <summary>
        /// 主设备发送心跳,从设备只回应
        /// </summary>
        protected virtual void SendHeartbeat() { }
        /// <summary>
        /// 处理发送失败
        /// </summary>
        /// <param name="ackState">ACK结果码(CTC_CMD_ACK)</param>
        /// <param name="dataPacket">发送的数据包</param>
        protected virtual void HandleSendFail(byte ackState, DataPacket dataPacket)
        {
            TotalSendErrorMsgCount++;
            switch (ackState)
            {
                case CTCMsg.CTC_ACK_UNSUPPORTED:
                    LogE("ACK-不支持指令");
                    break;
                case CTCMsg.CTC_ACK_LENGTH_ERR:
                    LogE("ACK-长度错误");
                    break;
                case CTCMsg.CTC_ACK_BUSY:
                    LogE("ACK-设备忙");
                    break;
                case CTCMsg.CTC_ACK_HEADER_ERR:
                    LogE("ACK-包头错误");
                    break;
                case CTCMsg.CTC_ACK_CRC_ERR:
                    LogE("ACK-CRC错误");
                    break;
                default:
                    // 未知ACK应答,不需要处理
                    break;
            }
            OnSendFail?.Invoke(ackState, dataPacket.Player, dataPacket.Inst, dataPacket.Data);
        }
        /// <summary>
        /// 处理发送成功
        /// </summary>
        /// <param name="dataPacket">发送的数据包</param>
        protected virtual void HandleSendSuccess(DataPacket dataPacket)
        {
            TotalSendMsgCount++;
            OnSendSuccess?.Invoke(dataPacket.Player, dataPacket.Inst, dataPacket.Data);
        }
        /// <summary>
        /// 处理接收数据
        /// </summary>
        /// <param name="dataPacket"></param>
        protected abstract void HandleReceive(DataPacket dataPacket);

        /// <summary>
        /// 检查指令是否相同
        /// </summary>
        /// <param name="inst0"></param>
        /// <param name="inst1"></param>
        protected bool CheckInst(byte[] inst0, byte[] inst1)
        {
            if (inst0 == null || inst1 == null)
            {
                return false;
            }
            if (inst0.Length != 2 || inst1.Length != 2)
            {
                return false;
            }
            if (inst0[0] != inst1[0])
            {
                return false;
            }
            if (inst0[1] != inst1[1])
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// ACK回应处理
        /// </summary>
        protected void HandleAckResponse(DataPacket dataPacket)
        {
            if (dataPacket.Data[2] == CTCMsg.CTC_ACK_SUCCESS)
            {
                HandleSendSuccess(SendDataPacket);
            }
            else
            {
                HandleSendFail(dataPacket.Data[2], SendDataPacket);
            }
            SendDataPacket = null;
        }
        /// <summary>
        /// 回应ACK
        /// </summary>
        /// <param name="dataPacket">收到的数据包</param>
        /// <param name="result">回应状态</param>
        protected void SendACK(DataPacket dataPacket, byte result)
        {
            if (result == CTCMsg.CTC_ACK_SUCCESS)
            {
                TotalReceivedMsgCount++;
            }
            else
            {
                TotalReceivedErrorMsgCount++;
            }
            DataPacket sendData = new DataPacket(dataPacket.Player, CTCMsg.CTC_CMD_ACK, dataPacket.TransactionId, dataPacket.Inst[0], dataPacket.Inst[1], result);
            SendACK(sendData);
        }
        protected void LogI(string msg)
        {
            DebugLogI?.Invoke(Player, msg);
        }
        protected void LogE(string msg)
        {
            DebugLogE?.Invoke(Player, msg);
        }
    }
}
